package com.lkd.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.lkd.business.MsgHandlerContext;
import com.lkd.common.VMSystem;
import com.lkd.config.TopicConfig;
import com.lkd.contract.SupplyChannel;
import com.lkd.contract.SupplyContract;
import com.lkd.contract.TaskCompleteContract;
import com.lkd.dao.TaskDao;
import com.lkd.emq.MqttProducer;
import com.lkd.entity.TaskDetailsEntity;
import com.lkd.entity.TaskEntity;
import com.lkd.entity.TaskStatusTypeEntity;
import com.lkd.exception.LogicException;
import com.lkd.feign.UserService;
import com.lkd.feign.VMService;
import com.lkd.http.vo.*;
import com.lkd.service.TaskDetailsService;
import com.lkd.service.TaskService;
import com.lkd.service.TaskStatusTypeService;
import com.lkd.vo.Pager;
import com.lkd.vo.UserVO;
import com.lkd.vo.UserWorkVO;
import com.lkd.vo.VmVO;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.weaver.ast.Var;
import org.joda.time.DateTime;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class TaskServiceImpl extends ServiceImpl<TaskDao,TaskEntity> implements TaskService{

    @Autowired
    private TaskStatusTypeService statusTypeService;

    @Autowired
    private RedisTemplate<String,Object> redisTemplate;

    @Autowired
    private VMService vmService;

    @Autowired
    private UserService userService;

    @Autowired
    private TaskDetailsService taskDetailsService;

    @Autowired
    private MqttProducer mqttProducer;
    @Override
    public Pager<TaskEntity> search(Long pageIndex, Long pageSize, String innerCode, Integer userId, String taskCode, Integer status, Boolean isRepair, String start, String end) {
        Page<TaskEntity> page = new Page<>(pageIndex,pageSize);
        LambdaQueryWrapper<TaskEntity> qw = new LambdaQueryWrapper<>();
        if(!Strings.isNullOrEmpty(innerCode)){
            qw.eq(TaskEntity::getInnerCode,innerCode);
        }
        if(userId != null && userId > 0){
            qw.eq(TaskEntity::getUserId,userId);
        }
        if(!Strings.isNullOrEmpty(taskCode)){
            qw.like(TaskEntity::getTaskCode,taskCode);
        }
        if(status != null && status > 0){
            qw.eq(TaskEntity::getTaskStatus,status);
        }
        if(isRepair != null){
            if(isRepair){
                qw.ne(TaskEntity::getProductTypeId, VMSystem.TASK_TYPE_SUPPLY);
            }else {
                qw.eq(TaskEntity::getProductTypeId,VMSystem.TASK_TYPE_SUPPLY);
            }
        }
        if(!Strings.isNullOrEmpty(start) && !Strings.isNullOrEmpty(end)){
            qw
                    .ge(TaskEntity::getCreateTime, LocalDate.parse(start, DateTimeFormatter.ISO_LOCAL_DATE))
                    .le(TaskEntity::getCreateTime,LocalDate.parse(end,DateTimeFormatter.ISO_LOCAL_DATE));
        }
        //根据最后更新时间倒序排序
        qw.orderByDesc(TaskEntity::getUpdateTime);
        return Pager.build(this.page(page,qw));
    }



    @Override
    public List<TaskStatusTypeEntity> getAllStatus() {
        QueryWrapper<TaskStatusTypeEntity> qw = new QueryWrapper<>();
        qw.lambda()
                .ge(TaskStatusTypeEntity::getStatusId,VMSystem.TASK_STATUS_CREATE);

        return statusTypeService.list();
    }


    @Transactional
    @Override
    public boolean createTask(TaskViewModel taskViewModel) throws LogicException {
        QueryWrapper<TaskEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .select(TaskEntity::getTaskId)
                .eq(TaskEntity::getInnerCode,taskViewModel.getInnerCode())
                .eq(TaskEntity::getProductTypeId,taskViewModel.getProductType())
                .le(TaskEntity::getTaskStatus,VMSystem.TASK_STATUS_PROGRESS);
        int count = count(queryWrapper);
        if(count>0){
            throw new LogicException("存在未完成同类型工单，不能创建");
        }
        //跨服务查询售货机微服务，得到售货机的地址和区域id
        VmVO vm = vmService.getVMInfo(taskViewModel.getInnerCode());
        if(vm==null){
            throw new LogicException("该机器不存在");
        }
        //跨服务查询用户微服务，得到用户名
        UserVO user = userService.getUser(taskViewModel.getUserId());
        if(user==null){
            throw new LogicException("该用户不存在");
        }

        decide(taskViewModel, vm);

        //新增工单表记录
        TaskEntity taskEntity = new TaskEntity();
        taskEntity.setTaskCode(generateTaskCode());//工单编号
        BeanUtils.copyProperties(taskViewModel,taskEntity);//复制属性
        taskEntity.setTaskStatus(VMSystem.TASK_STATUS_CREATE);//工单状态
        taskEntity.setProductTypeId(taskViewModel.getProductType());//工单类型
        taskEntity.setAddr(vm.getNodeAddr());//地址
        taskEntity.setRegionId(  vm.getRegionId() );//区域
        taskEntity.setUserName(user.getUserName());//用户名
        this.save(taskEntity);

        //如果是补货工单，向 工单明细表插入记录
        if(taskEntity.getProductTypeId() == VMSystem.TASK_TYPE_SUPPLY){
            taskViewModel.getDetails().forEach(d->{
                TaskDetailsEntity detailsEntity = new TaskDetailsEntity();
                BeanUtils.copyProperties( d,detailsEntity );
                detailsEntity.setTaskId(taskEntity.getTaskId());
                taskDetailsService.save(detailsEntity);
            });
        }

        updateByZSort(taskEntity,1);
        return true;
    }

    /**
     * 更新工单量列表
     * @param taskEntity
     * @param score
     */
    private void updateByZSort(TaskEntity taskEntity, int score) {
        String RoleId = "1003";//维修
        if(taskEntity.getProductTypeId().intValue()==2){
            RoleId="1002";//运营（补货）
        }
        String key = VMSystem.REGION_TASK_KEY_PREF+ LocalDateTime.now()
                .format(DateTimeFormatter.ofPattern("yyyyMMdd"))
                +"."+taskEntity.getRegionId()
                +"." +RoleId;
        redisTemplate.opsForZSet().incrementScore(key,taskEntity.getUserId(),score);
    }

    private void decide(TaskViewModel taskViewModel, VmVO vm) {
        if(VMSystem.TASK_TYPE_DEPLOY.equals(taskViewModel.getProductType())&&VMSystem.VM_STATUS_RUNNING.equals(vm.getVmStatus())){
                throw new LogicException("投放工单运营状态异常");
        }
        if(VMSystem.TASK_TYPE_SUPPLY.equals(taskViewModel.getProductType())&&!VMSystem.VM_STATUS_RUNNING.equals(vm.getVmStatus())){
                throw new LogicException("补货工单非运营状态异常");
        }
        if(VMSystem.TASK_TYPE_REPAIR.equals(taskViewModel.getProductType())&&!VMSystem.VM_STATUS_RUNNING.equals(vm.getVmStatus())){
                throw new LogicException("撤机工单运营状态异常");
        }
    }


    /**
     * 生成工单编号
     * @return
     */
    private String generateTaskCode(){
        //日期+序号
        String date = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));  //日期字符串
        String key= "lkd.task.code."+date; //redis key
        Object obj = redisTemplate.opsForValue().get(key);
        if(obj==null){
            redisTemplate.opsForValue().set(key,1L, Duration.ofDays(1) );
            return date+"0001";
        }
        return date+  Strings.padStart( redisTemplate.opsForValue().increment(key,1).toString(),4,'0');
    }

    @Transactional
    @Override
    public boolean accept(Long id) {
        TaskEntity task = this.getById(id);  //查询工单
        if (!VMSystem.TASK_STATUS_CREATE.equals(task.getTaskStatus())) {
            throw new LogicException("接收工单状态异常");
        }
        task.setTaskStatus( VMSystem.TASK_STATUS_PROGRESS );//修改工单状态为进行
        return this.updateById(task);
    }

    @Override
    public boolean cancelTask(Long id, CancelTaskViewModel cancelVM) {
        TaskEntity task = this.getById(id);  //查询工单
        if (VMSystem.TASK_STATUS_CANCEL.equals(task.getTaskStatus())||VMSystem.TASK_STATUS_FINISH.equals(task.getTaskStatus())) {
            throw new LogicException("取消工单状态异常");
        }
        task.setTaskStatus( VMSystem.TASK_STATUS_CANCEL  );
        task.setDesc(cancelVM.getDesc());
        updateByZSort(task,-1);
        return this.updateById(task);
    }

    @Override
    public boolean completeTask(long id) {
        TaskEntity taskEntity = this.getById(id);
        if (!VMSystem.TASK_STATUS_PROGRESS.equals(taskEntity.getTaskStatus())) {
            throw new LogicException("完成工单状态异常");
        }
        taskEntity.setTaskStatus(VMSystem.TASK_STATUS_FINISH);//修改工单状态
        this.updateById(taskEntity);
        if(VMSystem.TASK_TYPE_DEPLOY.equals(taskEntity.getProductTypeId())||VMSystem.TASK_TYPE_REVOKE.equals(taskEntity.getProductTypeId())){
            TaskCompleteContract taskCompleteContract = new TaskCompleteContract();
            taskCompleteContract.setTaskType(taskEntity.getProductTypeId());
            taskCompleteContract.setInnerCode(taskEntity.getInnerCode());
            try {
                mqttProducer.send(TopicConfig.VMS_COMPLETED_TOPIC,2,taskCompleteContract);
            } catch (JsonProcessingException e) {
                throw  new LogicException("根据工单修改售货机状态异常");
            }
        }
        noticeVMServiceSupply(taskEntity);
        return true;
    }

    @Override
    public int getLeastUser(Long regionId, boolean isRepair) {
        String RoleId = "1003";//维修
        if(!isRepair){
            RoleId="1002";//运营（补货）
        }
        String key = VMSystem.REGION_TASK_KEY_PREF+ LocalDateTime.now()
                .format(DateTimeFormatter.ofPattern("yyyyMMdd"))
                +"."+regionId
                +"." +RoleId;
        Set<Object> set = redisTemplate.opsForZSet().range(key, 0, 0);
        if(set==null||set.isEmpty()){
            return 0;
        }
        return (int) set.stream().collect(Collectors.toList()).get(0);
    }

    /**
     * 人员统计头部信息
     * @param start
     * @param end
     * @return
     */
    @Override
    public Object[] vmMessages(String start, String end) {
        Integer operatorCount = userService.getOperatorCount();
        Integer repairerCount = userService.getRepairerCount();
        QueryWrapper<TaskEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().between(TaskEntity::getCreateTime,start,end);
        List<TaskEntity> list = list(queryWrapper);
        if(list.isEmpty()){
            throw new LogicException("无工单列表");
        }
        TaskReportInfoVO task = new TaskReportInfoVO();
        List<TaskEntity> repair = list.stream().filter(c -> !c.getProductTypeId().equals(VMSystem.TASK_TYPE_SUPPLY)).collect(Collectors.toList());
        if(!repair.isEmpty()){
            task.setTotal(repair.size());
        }else {
            task.setTotal(0);
        }
        task.setRepair(false);
        task.setWorkerCount(repairerCount);
        TaskReportInfoVO supply = new TaskReportInfoVO();
        List<TaskEntity> supplyList = list.stream().filter(c -> c.getProductTypeId().equals(VMSystem.TASK_TYPE_SUPPLY)).collect(Collectors.toList());
        if (!supplyList.isEmpty()) {
            supply.setTotal(supplyList.size());
        }else{
            supply.setTotal(0);
        }
        supply.setRepair(true);
        supply.setWorkerCount(operatorCount);
        common(task, repair);
        common(supply,supplyList);
        return new Object[]{supply,task};
    }

    @Override
    public Object[] topTen(String start, String end, Boolean isRepair, String regionId) {
        QueryWrapper<TaskEntity> qq = new QueryWrapper<>();
        if(!regionId.equals("0")) {
            qq.lambda().eq(TaskEntity::getRegionId,regionId);
        }
        if(isRepair){
            qq.lambda().ne(TaskEntity::getProductTypeId,VMSystem.TASK_TYPE_SUPPLY);
        }else {
            qq.lambda().eq(TaskEntity::getProductTypeId,VMSystem.TASK_TYPE_SUPPLY);
        }
        String format = addtime(end);
        qq.lambda().between(TaskEntity::getCreateTime,start,format);
        List<TaskEntity> sum = list(qq);
        if(sum.isEmpty()){
            throw new LogicException("无工单列表");
        }
        List<TaskEntity> list = sum.stream().filter(c -> c.getTaskStatus().equals(VMSystem.TASK_STATUS_FINISH)).collect(Collectors.toList());
        Map<String, Integer> map = new HashMap<>();
        for (TaskEntity taskEntity : list) {

            Set<String> strings = map.keySet();
            if(strings.size()>0) {
                if (strings.contains(taskEntity.getUserName())) {
                    Integer integer = map.get(taskEntity.getUserName());
                    map.put(taskEntity.getUserName(),integer+1);
                }else {
                    map.put(taskEntity.getUserName(),1);
                }
            }else {
                map.put(taskEntity.getUserName(),1);
            }
        }

        List<Map.Entry<String, Integer>> list2 = new ArrayList<>(map.entrySet());
        Collections.sort(list2, (o1, o2) -> {
            //降序
            return o2.getValue().compareTo(o1.getValue());
        });
        ArrayList<Object> objects = new ArrayList<>();
        Iterator<Map.Entry<String, Integer>> iterator = list2.iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, Integer> entry = iterator.next();
            String key = entry.getKey();
            int value = entry.getValue();
            UserWorkVO userWorkVO = new UserWorkVO();
            userWorkVO.setUserName(key);
            userWorkVO.setWorkCount(value);
            objects.add(userWorkVO);
        }
        Object[] objects1 = objects.toArray(new Object[objects.size()]);
        return objects1;
    }

    private String addtime(String end) {
        DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
        Date dd = null;
        try {
            dd = df.parse(end);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(dd);
        calendar.add(Calendar.DAY_OF_MONTH, 1);
        String format = df.format(calendar.getTime());
        return format;
    }

    @Override
    public Object[] collectReport(String start, String end2) {
        List<String> timeStringList = timeCacular(start, end2);
        ArrayList<Object> objects = new ArrayList<>();
        if(timeStringList.size()==1){
            String one = timeStringList.get(0);
            String addtime = addtime(one);
            QueryWrapper<TaskEntity> queryWrapper = new QueryWrapper<>();
            queryWrapper.lambda().between(TaskEntity::getCreateTime,one,addtime);
            timeResult(objects, one, queryWrapper);
        }else {
            for (String s : timeStringList) {
                String addtime = addtime(s);
                QueryWrapper<TaskEntity> queryWrapper = new QueryWrapper<>();
                queryWrapper.lambda().between(TaskEntity::getCreateTime, s, addtime);
                timeResult(objects, s, queryWrapper);
            }
        }
        Object[] objects1 = objects.toArray(new Object[objects.size()]);
        return objects1;

    }

    private List<String> timeCacular(String start, String end2) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        Date begin = null;
        try {
            begin = sdf.parse(start);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        Date end = null;
        try {
            end = sdf.parse(end2);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        List<Date> lDate = new ArrayList();
        lDate.add(begin);
        Calendar calBegin = Calendar.getInstance();
        // 使用给定的 Date 设置此 Calendar 的时间
        calBegin.setTime(begin);
        Calendar calEnd = Calendar.getInstance();
        // 使用给定的 Date 设置此 Calendar 的时间
        calEnd.setTime(end);
        // 测试此日期是否在指定日期之后
        while (end.after(calBegin.getTime()))
        {
            // 根据日历的规则，为给定的日历字段添加或减去指定的时间量
            calBegin.add(Calendar.DAY_OF_MONTH, 1);
            lDate.add(calBegin.getTime());
        }
        List<String> timeStringList=new ArrayList<>();
        for (Date datePes :lDate){
            String format = sdf.format(datePes);
            timeStringList.add(format);
        }
        return timeStringList;
    }

    @Override
    public UserWorkVO userWork(Integer userId,String start,String end) {
        QueryWrapper<TaskEntity> queryWrapper = new QueryWrapper<>();
        String addtime = addtime(end);
        queryWrapper.lambda().eq(TaskEntity::getUserId,userId).between(TaskEntity::getCreateTime,start,addtime);
        List<TaskEntity> list = list(queryWrapper);
        return detail(userId, list);
    }

    private UserWorkVO detail(Integer userId, List<TaskEntity> list) {
        UserWorkVO userWorkVO = new UserWorkVO();
        List<TaskEntity> completed = list.stream().filter(c -> c.getTaskStatus().equals(VMSystem.TASK_STATUS_FINISH)).collect(Collectors.toList());
        if(!completed.isEmpty()){
            userWorkVO.setWorkCount(completed.size());
        }else {
            userWorkVO.setWorkCount(0);
        }
        List<TaskEntity> cancel = list.stream().filter(c -> c.getTaskStatus().equals(VMSystem.TASK_STATUS_CANCEL)).collect(Collectors.toList());
        if(!cancel.isEmpty()){
            userWorkVO.setCancelCount(cancel.size());
        }else {
            userWorkVO.setCancelCount(0);
        }
        userWorkVO.setTotal(list.size());
        userWorkVO.setUserId(userId);
        return userWorkVO;
    }


    private void timeResult(ArrayList<Object> objects, String t, QueryWrapper<TaskEntity> queryWrapper) {
        List<TaskEntity> list = list(queryWrapper);
        if(list.isEmpty()){
            return;
        }
        TaskCollectVo taskCollectVo = new TaskCollectVo();
        List<TaskEntity> finish = list.stream().filter(c -> c.getTaskStatus().equals(VMSystem.TASK_STATUS_FINISH)).collect(Collectors.toList());
        if(!finish.isEmpty()){
            taskCollectVo.setFinishCount(finish.size());
        }else {
            taskCollectVo.setFinishCount(0);
        }
        List<TaskEntity> cancel = list.stream().filter(c -> c.getTaskStatus().equals(VMSystem.TASK_STATUS_CANCEL)).collect(Collectors.toList());
        if(!cancel.isEmpty()){
            taskCollectVo.setCancelCount(cancel.size());
        }else {
            taskCollectVo.setCancelCount(0);
        }
        List<TaskEntity> progress = list.stream().filter(c -> c.getTaskStatus().equals(VMSystem.TASK_STATUS_PROGRESS)).collect(Collectors.toList());
        if(!progress.isEmpty()){
            taskCollectVo.setProgressCount(progress.size());
        }else {
            taskCollectVo.setProgressCount(0);
        }
        taskCollectVo.setCollectDate(t);
        objects.add(taskCollectVo);
    }


    private void common(TaskReportInfoVO task, List<TaskEntity> repair) {
        List<TaskEntity> completed = repair.stream().filter(c -> c.getTaskStatus().equals(VMSystem.TASK_STATUS_FINISH)).collect(Collectors.toList());
        if(!completed.isEmpty()){
            task.setCompletedTotal(completed.size());
        }else {
            task.setCompletedTotal(0);
        }
        List<TaskEntity> cancel = repair.stream().filter(c -> c.getTaskStatus().equals(VMSystem.TASK_STATUS_CANCEL)).collect(Collectors.toList());
        if(!cancel.isEmpty()){
            task.setCancelTotal(cancel.size());
        }else {
            task.setCancelTotal(0);
        }
    }


    /**
     * 补货协议封装与下发
     * @param taskEntity
     */
    private void noticeVMServiceSupply(TaskEntity taskEntity){

        //协议内容封装
        //1.根据工单id查询工单明细表
        QueryWrapper<TaskDetailsEntity> qw = new QueryWrapper<>();
        qw.lambda()
                .eq(TaskDetailsEntity::getTaskId,taskEntity.getTaskId());
        List<TaskDetailsEntity> details = taskDetailsService.list(qw);
        //2.构建协议内容
        SupplyContract supplyContract = new SupplyContract();
        supplyContract.setInnerCode(taskEntity.getInnerCode());//售货机编号
        List<SupplyChannel> supplyChannels = Lists.newArrayList();//补货数据
        //从工单明细表提取数据加到补货数据中
        details.forEach(d->{
            SupplyChannel channel = new SupplyChannel();
            channel.setChannelId(d.getChannelCode());//货道编号
            channel.setCapacity(d.getExpectCapacity());//补货数量
            supplyChannels.add(channel);
        });
        supplyContract.setSupplyData(supplyChannels);

        //3.下发补货协议
        //发送到emq
        try {
            mqttProducer.send( TopicConfig.VMS_SUPPLY_TOPIC,2, supplyContract );
        } catch (Exception e) {
            log.error("发送工单完成协议出错");
            throw new LogicException("发送工单完成协议出错");
        }
    }
    private void extracted(TaskEntity taskEntity) {
        if(VMSystem.TASK_TYPE_SUPPLY.equals(taskEntity.getProductTypeId())){
            SupplyContract supplyContract = new SupplyContract();
            List<SupplyChannel> supplyChannels = new ArrayList<>();
            SupplyChannel supplyChannel = new SupplyChannel();
            List<TaskDetailsEntity> entityList = taskDetailsService.getByTaskId(taskEntity.getTaskId());
            for (TaskDetailsEntity taskDetailsEntity : entityList) {
                supplyChannel.setChannelId(taskDetailsEntity.getChannelCode());
                supplyChannel.setCapacity(taskDetailsEntity.getExpectCapacity());
                supplyChannel.setSkuId(taskDetailsEntity.getSkuId());
                supplyChannel.setSkuName(taskDetailsEntity.getSkuName());
                supplyChannel.setSkuImage(taskDetailsEntity.getSkuImage());
                supplyChannels.add(supplyChannel);
            }
            supplyContract.setSupplyData(supplyChannels);
            supplyContract.setInnerCode(taskEntity.getInnerCode());
            try {
                mqttProducer.send(TopicConfig.VMS_SUPPLY_TOPIC,2,supplyContract);
            } catch (JsonProcessingException e) {
                throw new LogicException("根据补货修改售货机状态异常");
            }

        }
    }

}
